﻿using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using Zeta.Common;
using Zeta.Game;
using Zeta.Game.Internals.Actors;
using Zeta.Game.Internals.SNO;

namespace LocationTracker
{

    internal static class Cache
    {

        internal static Dictionary<int, List<ActorLocation>> cache;
        internal static List<ActorLocation> currentLevelCache;
        
        internal static int activeBountyId;

        /// <summary> 
        /// Gets objects from current actor list in DemonBuddy
        /// </summary> 
        internal static void Update()
        {
            var diaObjects = ZetaDia.Actors.RActorList.OfType<DiaObject>();
            
            activeBountyId = ZetaDia.ActInfo.ActiveBounty != null && ZetaDia.ActInfo.ActiveBounty.Info.IsValid ? ZetaDia.ActInfo.ActiveBounty.Info.QuestSNO : 0;

            List<DiaObject> mobs = diaObjects.Where(r => r.ActorType == ActorType.Monster || r.ActorType == ActorType.Gizmo).ToList();

            foreach (DiaObject mob in mobs)
            {
                Cache.Track(mob);
            }
        }

        /// <summary> 
        /// Checks a single actor and records if valid
        /// </summary> 
        internal static void Track(DiaObject actor)
        {
            if (cache == null)
            {
                Reset();
            }            

            if (Reference.BlackListIds.Contains(actor.ActorSNO))
            {
                return;
            }

            var aloc = CreateActorLocation(actor);
            var currentLevelAreaId = ZetaDia.CurrentLevelAreaId;

            if (aloc != null && aloc.Type != ActorLocationType.Unknown)
            {
                if (!IsDuplicate(aloc.ActorSNO))
                {
                    if (!cache.Keys.Contains(currentLevelAreaId))
                    {
                        cache.Add(currentLevelAreaId, new List<ActorLocation>());
                    }

                    cache[currentLevelAreaId].Add(aloc);
                    currentLevelCache.Add(aloc);

                    //Logger.Log("found " + aloc.Type + ": " + raw.Name + " " + raw.CommonData.AsRActor.Name + " Found at x:" + raw.Position.X + " y:" + raw.Position.X + " z:" + raw.Position.Z + " on world: " + ZetaDia.WorldInfo.SNOId + (aloc.IsQuestMonster ? "QUEST MONSTER" : "") + (aloc.IsBountyObjective ? "BOUNTY OBJECTIVE" : ""));                

                }
            }

        }

        internal static ActorLocation CreateActorLocation(DiaObject actor){

            ActorLocation aloc;

            //if (actor.CommonData == null || !actor.CommonData.IsValid || actor.RActorGuid == -1)
            if (actor.CommonData == null || !actor.CommonData.IsValid)
            {
                return null;
            }

            try
            {

                aloc = new ActorLocation
                {
                    Name = actor.CommonData.Name,
                    DiaObjectName = actor.Name,
                    Position = actor.CommonData.Position,
                    ActorSNO = actor.ActorSNO,
                    X = (int)Convert.ToDouble(actor.CommonData.Position.X, CultureInfo.InvariantCulture.NumberFormat),
                    Y = (int)Convert.ToDouble(actor.CommonData.Position.Y, CultureInfo.InvariantCulture.NumberFormat),
                    Z = (int)Convert.ToDouble(actor.CommonData.Position.Z, CultureInfo.InvariantCulture.NumberFormat),
                    IsElite = actor.CommonData.IsElite,
                    IsRare = actor.CommonData.IsRare,
                    IsUnique = actor.CommonData.IsUnique,
                    IsBountyObjective = (activeBountyId > 0 && actor.CommonData.GetAttribute<int>(ActorAttributeType.BountyObjective) > 0),
                    IsGoblin = Reference.GoblinIds.Contains(actor.ActorSNO),
                    IsCursed = (actor.Name.Contains("CursedChest") || actor.Name.Contains("CursedShrine")),
                    IsPortal = (actor.ActorInfo.GizmoType == Zeta.Game.Internals.SNO.GizmoType.DungeonPortal),
                    BountyObjectiveQuestId = activeBountyId
                
                };



                //bool isRareChest = (aloc.DiaObjectName.ToLower().Contains("chest_rare"));
                //bool isChest = (!isRareChest && aloc.DiaObjectName.ToLower().Contains("chest"));


                //if(aloc.aActorInfo.GizmoType == GizmoType.ReturnPortal &&
                //         o.Position.Distance2D(ZetaDia.Me.Position) <= 30f);


                //Player.InActiveEvent = ZetaDia.ActInfo.ActiveQuests.Any(q => DataDictionary.EventQuests.Contains(q.QuestSNO));

                //try
                //{
                //    aloc.IsQuestMonster = (raw.CommonData.GetAttribute<int>(ActorAttributeType.QuestMonster) > 1);
                //}
                //catch (Exception ex)
                //{
                //    Logger.Log("Error reading IsQuestMonster for Unit sno:{0} raGuid:{1} name:{2} ex:{3}", raw.ActorSNO, raw.RActorGuid, raw.Name, ex.Message);
                //}

                return aloc;

            }
            catch (Exception ex)
            {
                //Logger.Log("Error creating actorLocation for Unit sno:{0} raGuid:{1} name:{2} balancetype:{3} actortype:{4} ex:{5}", actor.ActorSNO, actor.RActorGuid, actor.Name, actor.CommonData.GameBalanceType, actor.ActorType, ex.Message);
            }


            return null;

        }

        /// <summary> 
        /// Checks against the current level cache for duplicate
        /// </summary> 
        internal static bool IsDuplicate(int actorSNO)
        {
            return (currentLevelCache != null && currentLevelCache.Any(m => m.ActorSNO == actorSNO));
        }

        internal static bool IsProperCached(DiaObject raw, int levelAreaId)
        {
            return (cache != null && cache.Keys.Contains(levelAreaId) && cache[levelAreaId].Any(m => m.ActorSNO == raw.ActorSNO));
        }

        /// <summary> 
        /// Full clear of cache
        /// </summary> 
        internal static void Reset()
        {
            cache = new Dictionary<int, List<ActorLocation>>();
            ResetLevel();
        }

        /// <summary> 
        /// Clear the current level cache
        /// </summary> 
        internal static void ResetLevel()
        {
            currentLevelCache = new List<ActorLocation>();
        }

        /// <summary> 
        /// Saves cache to files and clears it.
        /// </summary> 
        internal static void DumpToFiles()
        {
            Dumper.Save();
            Cache.Reset();
        }

        /// <summary> 
        /// Outputs cache to console
        /// </summary> 
        internal static void DumpToConsole()
        {
            //if (Zeta.Bot.BotMain.IsRunning)
            //{
            //    BotMain.Stop();
            //}

            //Thread.Sleep(500);

            try
            {

                var cache = Cache.cache;

                if (cache != null && cache.Count > 0)
                {


                    foreach (var worldId in cache.Keys)
                    {
                        Logger.Log("//==== Mobs for worldId: " + worldId + "====");

                        foreach (var mob in cache[worldId])
                        {

                            //Logger.Log(mob.Type + " " + mob.Name + " Found at ["+mob.X + "," + mob.Y+"] WorldId: " + ZetaDia.WorldInfo.SNOId);

                            if (mob.IsElite)
                            {
                                Logger.Log("[" + mob.Y + "," + mob.X + ",'" + mob.Name + "',null,'',null,'',null,'',null,'',null,'',null,''],");
                            }

                            if (mob.IsUnique)
                            {
                                Logger.Log("[" + mob.Y + ",null,''," + mob.X + ",'" + mob.Name + "',null,'',null,'',null,'',null,'',null,''],");
                            }

                            if (mob.IsRare)
                            {
                                Logger.Log("[" + mob.Y + ",null,'',null,''," + mob.X + ",'" + mob.Name + "',null,'',null,'',null,'',null,''],");
                            }

                            if (mob.IsGoblin)
                            {
                                Logger.Log("[" + mob.Y + ",null,'',null,'',null,''," + mob.X + ",'" + mob.Name + "',null,'',null,'',null,''],");
                            }

                            if (mob.IsBountyObjective)
                            {
                                Logger.Log("[" + mob.Y + ",null,'',null,'',null,'',null,''," + mob.X + ",'" + mob.Name + "',null,'',null,''],");
                            }

                            if (mob.IsCursed)
                            {
                                Logger.Log("[" + mob.Y + ",null,'',null,'',null,'',null,'',null,''," + mob.X + ",'" + mob.Name + "',null,''],");
                            }

                            if (mob.IsPortal)
                            {
                                Logger.Log("[" + mob.Y + ",null,'',null,'',null,'',null,'',null,'',null,''," + mob.X + ",'" + mob.Name + "'],");
                            }


                        }

                    }
                }
                else
                {
                    Logger.Log("Cache is empty");
                }

            }
            catch (Exception ex)
            {
                Logger.Log(ex.ToString());
            }
        }



    }



}
